package org.light.utils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.logging.log4j.util.Strings;
import org.light.core.Writeable;
import org.light.domain.Domain;
import org.light.domain.Dropdown;
import org.light.domain.Field;
import org.light.domain.ManyToMany;
import org.light.domain.ManyToManyCandidate;
import org.light.domain.Statement;
import org.light.domain.StatementList;
import org.light.exception.ValidateException;

public class DomainUtil {
	public static Domain findDomainInList(List<Domain> targets, String domainName) throws ValidateException{
		for (Domain d: targets){
			if (d.getStandardName().equals(domainName)) return d;
		}
		throw new ValidateException("域对象"+domainName+"不在列表中！");
	}
	
	public static Domain findDomainInListOrReturnNull(List<Domain> targets, String domainName){
		for (Domain d: targets){
			if (d.getStandardName().equals(domainName)) return d;
		}
		return null;
	}

	public static Domain findDomainInListReturnNull(List<Domain> targets, String domainName){
		for (Domain d: targets){
			if (d.getStandardName().equals(domainName)) return d;
		}
		return null;
	}
	
	public static Boolean inDomainList(Domain d,List<Domain> list){
		for (Domain dn :list){
			if (d.getStandardName().equals(dn.getStandardName())){
				return true;
			}
		}
		return false;
	}
	
	public static Boolean nameExistsInDomainList(String domainName,List<Domain> list){
		for (Domain dn :list){
			if (domainName.equals(dn.getStandardName())){
				return true;
			}
		}
		return false;
	}
	
	public static Boolean memberInDomainSet(Set<Domain> targets,Set<Domain> set){
		for (Domain target:targets) {
			for (Domain dn :set){
				if (target.getStandardName().equals(dn.getStandardName())){
					return true;
				}
			}
		}
		return false;
	}
	
	public static Set<String> domainSetToNameSet(Set<Domain> domains){
		Set<String> names = new TreeSet<>();
		for (Domain d:domains) {
			names.add(d.getDomainName().getFieldValue());
		}
		return names;
	}
	
	public static String domainSetToNameSetStr(Set<Domain> domains){
		StringBuilder namesStr = new StringBuilder();
		List<String> names = new ArrayList<>();
		for (Domain d:domains) {
			names.add(d.getDomainName().getFieldValue());
		}
		if (names.size()>1) {
			namesStr.append("{");
			for (String name:names) {
				namesStr.append(name).append(",");
			}
			namesStr.substring(0, namesStr.length()-1);
			namesStr.append("}");
		}else if (names.size()==1) {
			namesStr.append(names.get(0));
		}		
		return namesStr.toString();
	}
	
	public static Boolean inDomainSet(Domain d,Set<Domain> set){
		for (Domain dn :set){
			if (d.getStandardName().equals(dn.getStandardName())){
				return true;
			}
		}
		return false;
	}
	
	public static List<Domain> filterDataDomainList(List<List<Domain>> dataDomains, String domainName) throws ValidateException{
		List<Domain> results = new ArrayList<Domain>();
		for (List<Domain> list:dataDomains) {
			for (Domain d: list){
				if (d.getStandardName().equals(domainName)) results.add(d);
			}
		}
		return results;
	}
	
	public static  List<List<Domain>> removeDataDomainsFromLists(List<List<Domain>> dataDomains, String domainName) throws ValidateException{
		List<List<Domain>> results = new ArrayList<>();
		for (List<Domain> list:dataDomains) {	
			List<Domain> rows = new ArrayList<>();
			for (Domain d:list) {				
				if (!d.getStandardName().equals(domainName)) rows.add(d);
			}
			if (rows.size()>0)results.add(rows);
		}
		return results;
	}
	
	public static List<List<Domain>> addDataDomainToList(List<List<Domain>> dataDomains, Domain datadomain) throws ValidateException{
		List<List<Domain>> results = new ArrayList<>();
		boolean found = false;
		for (List<Domain> list:dataDomains) {
			if (list.size()>0) {
				if (list.get(0).getStandardName().equals(datadomain.getStandardName())) {
					list.add((Domain)datadomain.deepClone());
					found =true;
				}
				results.add(list);
			}
		}
		if (!found) {
			List<Domain> list = new ArrayList<>();
			list.add((Domain)datadomain.deepClone());
			results.add(list);
		}
		return results;
	}
	
	public static List<List<Domain>> deleteDataDomainFromList(List<List<Domain>> dataDomains, String datadomainname, int pos) throws ValidateException{
		List<List<Domain>> results = new ArrayList<>();
		for (List<Domain> list:dataDomains) {
			if (list==null||list.size()==0) continue;
			if (list.get(0).getStandardName().equals(datadomainname)&&list.size()>pos) {
				list.remove(pos);				
			}
			results.add(list);
		}
		return results;
	}
	
	public static List<List<Domain>> deleteAllDataDomainsFromList(List<List<Domain>> dataDomains, String datadomainname) throws ValidateException{
		List<List<Domain>> results = new ArrayList<>();
		for (List<Domain> list:dataDomains) {
			List<Domain> resultList = new ArrayList<>();
			for (Domain d: list) {
				if (!d.getStandardName().equals(datadomainname)) resultList.add(d);
			}
			if (resultList.size()>0) results.add(resultList);
		}
		return results;
	}
	
	public static List<List<Domain>> updateDataDomainFromList(List<List<Domain>> dataDomains, Domain datadomain,int pos) throws ValidateException{
		List<List<Domain>> results = new ArrayList<>();
		for (List<Domain> list:dataDomains) {
			if (list == null || list.size()==0) continue;
			if (list.get(0).getStandardName().equals(datadomain.getStandardName())&&list.size()>pos) list.set(pos, (Domain)datadomain.deepClone());
			results.add(list);
		}
		return results;
	}
	
	public static boolean validateId(String val) {
		if (StringUtil.isBlank(val)) return true;
		boolean flag = false;
		try {
			String regex = "^-?\\d+$";
			Pattern p = Pattern.compile(regex);
			Matcher m = p.matcher(val);
			if (m.find()) {
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return flag;
	}
	
	public static Domain findDataDomainInListByDomainNameValue(List<Domain> targets,String nameValue) {
		if (!StringUtil.isBlank(nameValue)) {
			for (Domain d:targets) {
				if (d.getDomainName().getFieldValue().equals(nameValue))
					return d;
			}
		}
		return null;
	}
	
	public static Boolean setContainsDomain(Set<Domain> set, String domainName) {
		for (Domain d : set) {
			if (d.getStandardName().equals(domainName))
				return true;
		}
		return false;
	}
	
	public static Domain lookupDoaminInSet(Set<Domain> set, String domainName) {
		for (Domain d : set) {
			if (d.getStandardName().equals(domainName)&&!d.isLegacy())
				return d;
		}
		return null;
	}
	
	public static Field findDomainFieldByFieldName(Domain d,String fieldName) {
		for (Field f:d.getFields()) {
			if (f.getFieldName().equals(fieldName)) return f;
		}
		return null;
	}	
	
	public static boolean expendFixedName(Domain d) {
		for (Field f:d.getFields()) {
			String fieldName = f.getFieldName();
			String fixedName = f.getFixedName();
			if (!StringUtil.isBlank(fixedName)&&!fixedName.equals(fieldName)) return true;
		}
		return false;
	}
	
	public static StatementList generateImportStatements(Set<String> imports) {
		List<Writeable> sList = new ArrayList<Writeable>();
		long serial = 1000L;
		for (String s : imports) {
			sList.add(new Statement(serial,0,"use " +s+";"));
			serial += 1000L;
		}
		sList.add(new Statement(serial,0,""));
		return WriteableUtil.merge(sList);
	}
	
	public static List<ManyToMany> filterMtms(List<Domain> dummyDb, ManyToMany mtm) {
		List<ManyToMany> results = new ArrayList<>();
		for (Domain d:dummyDb) {
			for (ManyToMany dmtm: d.getManyToManies()) {
				if (dmtm.getStandardName().equals(mtm.getStandardName())) {
					results.add(dmtm);
				}
			}
		}
		return results;
	}

	public static int findDomainSerialInList(List<Domain> targets, String domainName){
		for (int i=0;i<targets.size();i++){
			if (targets.get(i).getStandardName().equals(domainName)) return i;
		}
		return -1;
	}
	
	public static Domain attachMtmCandidateToDomain(Domain domain, ManyToManyCandidate mtmc) throws ValidateException{
		if (!domain.getStandardName().equals(mtmc.getMaster().getStandardName())) throw new ValidateException("多对多候选与域对象不匹配！");
		for (ManyToMany mtm :domain.getManyToManies()) {
			if (mtm.getManyToManySalveName().equals(mtmc.getSlave().getStandardName()) && mtm.getSlaveAlias().equals(mtmc.toManyToMany().getSlaveAlias())) return domain;
		}
		domain.addManyToMany(mtmc.toManyToMany());
		return domain;
	}

	public static List<Domain> attachMtmCandidates(final List<Domain> domains, final List<ManyToManyCandidate> mtmCandidateValues) throws ValidateException{
		Iterator<ManyToManyCandidate> mtmcIter = mtmCandidateValues.iterator();
		while (mtmcIter.hasNext()) {
			ManyToManyCandidate mtmc = mtmcIter.next();
			ListIterator<Domain> dIter = domains.listIterator();
outer:		while (dIter.hasNext()) {
				Domain d = dIter.next();
				if (d.getStandardName().equals(mtmc.getMasterName())) {
					if (d.getDomainId().getFieldValue().equals(mtmc.getMasterIdValue())) {
						for (final ManyToMany mtm :d.getManyToManies()) {
							if (mtm.getManyToManySalveName().equals(mtmc.getSlave().getStandardName()) && mtm.getSlaveAlias().equals(mtmc.toManyToMany().getSlaveAlias())) {
								mtm.setValues(mergeQuotedStrings(mtm.getValues(),mtmc.getSlaveIdValues()));
								break outer;
							}
						}
						d.addManyToMany(mtmc.toManyToMany());
					} 
				}				
			}
		}
		mtmcIter = mtmCandidateValues.iterator();
		while (mtmcIter.hasNext()) {
			ManyToManyCandidate mtmc = mtmcIter.next();
			Domain d = domains.get(0);
			if (!listContainsId(domains,mtmc.getMasterIdValue())) {
				Domain dd = (Domain) d.deepClone();
				dd.getDomainId().setFieldValue(mtmc.getMasterIdValue());
				dd.getDomainName().setFieldValue(dd.getStandardName()+"_"+mtmc.getMasterIdValue());
				dd.getActive().setFieldValue(dd.getDomainActiveStr());
				for (Field f: dd.getPlainFields()) {
					f.setFieldValue("");
				}
				dd.addManyToMany(mtmc.toManyToMany());
				domains.add(dd);
			}
		}
		return domains;
	}

	public static boolean listContainsId(List<Domain> domains, String idValue) {
		ListIterator<Domain> dIter = domains.listIterator();
		while (dIter.hasNext()) {
			Domain d = dIter.next();
			if (d.getDomainId().getFieldValue().equals(idValue)) return true;
		}
		return false;
	}

	public static String mergeQuotedStrings(String str1, String str2) {
		String [] strSet1 = str1.split(",");
		String [] strSet2 = str2.split(",");
		Set<String> intsSet = new TreeSet<>();
		for (String str : strSet1) {
			if (StringUtil.isInteger(str)) intsSet.add(str);
		}
		for (String str : strSet2) {
			if (StringUtil.isInteger(str)) intsSet.add(str);
		}
		String result = Strings.join(intsSet, ',');
		if (result.endsWith(",")) result = result.substring(0,result.length()-1);
		return result;		
	}

	public static List<List<Domain>> replaceSubDataDomainListInList(List<List<Domain>> dataDomains, List<Domain> subDataDomains) throws ValidateException{
		for (int i=0;i<dataDomains.size();i++) {			
			if (dataDomains.get(i).get(0).getStandardName().equals(subDataDomains.get(0).getStandardName())) {				
				dataDomains.set(i,subDataDomains);
			}
		}
		return dataDomains;
	}

	public static ManyToManyCandidate switchDomainToManyToManyCandidate(Domain domain,List<Domain> domains) throws Exception{
		Set<Field> fs = domain.getFields();
		if (fs.size() != 2) throw new ValidateException("多对多候选只能有两个字段。");
		for (Field f : fs) {
			if (!f.getFieldType().equalsIgnoreCase("Long") && !(f instanceof Dropdown)) throw new ValidateException("多对多候选字段类型不对。");
		}
		for (Field f : fs) {
			if (!f.getFieldName().endsWith("Id")) throw new ValidateException("多对多候选字段命名不对。");
		}
		
		List<Domain> masters = findTargetDomainListByFields(domains,fs);
		for (Domain master:masters){
			for (Field f : fs) {
				if (f.getFieldName().equals(master.getLowerFirstDomainName()+"Id")||f.getFieldName().equals(master.getLowerFirstPlural()+"Id")) {
					ManyToManyCandidate mtmc = new ManyToManyCandidate();
					Field masterId = f;
					Field slaveId = null;
					for (Field f2 :fs) {
						if (!f2.getFieldName().equals(masterId.getFieldName())) slaveId = f2;
					}
					mtmc.setMaster(master);
					mtmc.setMasterId(masterId);
					mtmc.setMasterName(master.getStandardName());
					mtmc.setMasterIdValue(masterId.getFieldValue());
					String mtmcLabel = domain.getLabel();
					if (mtmcLabel.startsWith("链接")) mtmcLabel = mtmcLabel.substring("链接".length(), mtmcLabel.length());
					else if (mtmcLabel.startsWith("Link")) mtmcLabel = mtmcLabel.substring("Link".length(), mtmcLabel.length());
					if (mtmcLabel.startsWith(master.getText())) mtmcLabel = mtmcLabel.substring(master.getText().length(),mtmcLabel.length());
					mtmc.setLabel(mtmcLabel);	
					if (slaveId != null) {						
						mtmc.setSlaveId(slaveId);						
						mtmc.setSlaveAlias(slaveId.getCapFirstFieldName().substring(0,slaveId.getFieldName().length()-2));
						mtmc.setSlaveIdValues(slaveId.getFieldValue());					
					}
					return mtmc;
				}
			}
		}
		throw new ValidateException("转换多对多候选错误。");
	}
	
	public static List<Domain> findTargetDomainListByFields(List<Domain> targets, Set<Field> idFields){
		List<Domain> results = new ArrayList<>();		
		for (Field f: idFields) {
			for (Domain d:targets) {
				if (f.getFieldName().endsWith("Id") && d.getLowerFirstDomainName().equals(f.getFieldName().substring(0,f.getFieldName().length()-2))) {
					results.add(d);
				}
			}
		}
		return results;
	}

	public static  List<List<Domain>> switchDataDomainsEnumToDomainFromLists(List<List<Domain>> dataDomains, String domainName) throws Exception{
		List<List<Domain>> results = new ArrayList<>();
		for (List<Domain> list:dataDomains) {	
			List<Domain> rows = new ArrayList<>();
			for (Domain d:list) {				
				if (d.getStandardName().equals(domainName)) {
					if (!(d instanceof org.light.domain.Enum)) throw new ValidateException("对象并非枚举！");
					rows.add(((org.light.domain.Enum)d).toDomain());
				}
				else rows.add(d);
			}
			if (rows.size()>0)results.add(rows);
		}
		return results;
	}

	public static  List<List<Domain>> switchDataDomainsToEnumFromLists(List<List<Domain>> dataDomains, String domainName) throws Exception{
		List<List<Domain>> results = new ArrayList<>();
		for (List<Domain> list:dataDomains) {	
			List<Domain> rows = new ArrayList<>();
			for (Domain d:list) {				
				if (d.getStandardName().equals(domainName)) {
					if (d instanceof org.light.domain.Enum) throw new ValidateException("对象并非域对象！");
					rows.add(d.toEnum());
				}
				else rows.add(d);
			}
			if (rows.size()>0)results.add(rows);
		}
		return results;
	}
}
